package ray.shader;

import java.util.ArrayList;
import ray.math.Color;
import ray.math.Point3;
import ray.math.Vector3;
import ray.surface.Surface;
import ray.Light;
import ray.Scene;
import ray.Intersection;
import ray.Ray;

/**
 * A Phong material. Uses the Modified Blinn-Phong model which is energy
 * preserving and reciprocal.
 *
 * @author ags
 */
public class Phong implements Shader {
	
	/** The color of the diffuse reflection. */
	protected final Color diffuseColor = new Color(1, 1, 1);
	public void setDiffuseColor(Color diffuseColor) { this.diffuseColor.set(diffuseColor); }
	
	/** The color of the specular reflection. */
	protected final Color specularColor = new Color(1, 1, 1);
	public void setSpecularColor(Color specularColor) { this.specularColor.set(specularColor); }
	
	/** The exponent controlling the sharpness of the specular reflection. */
	protected double exponent = 1.0;
	public void setExponent(double exponent) { this.exponent = exponent; }
	
	public Phong() { }
	
	public void shade(Color colorOut, Scene scene, ArrayList<Light> lights, Vector3 eye, Intersection inter) {
		
		Vector3 norm = new Vector3(inter.normal);
		Vector3 eyeDir = new Vector3(eye);
		norm.normalize();
		eyeDir.normalize();
		
		Point3 intersect = new Point3(inter.intersection);
		
		Color temp1 = new Color(0,0,0);
		
		for (int i=0; i<lights.size(); i++) 
		{	
			if(!isShadowed(scene, lights.get(i), inter, scene.getSurfaces()))
			{
				Color temp2 = new Color(diffuseColor.r, diffuseColor.g, diffuseColor.b);
				Color temp3 = new Color(specularColor.r, specularColor.g, specularColor.b);
				
				Vector3 lightRay = new Vector3(lights.get(i).position.x - intersect.x, lights.get(i).position.y - intersect.y, lights.get(i).position.z - intersect.z);
				lightRay.normalize();
				
				//the half vector
				Vector3 h = new Vector3();
				h.add(eyeDir, lightRay);
				h.normalize();
				
				//diffuse factor
				temp2.scale(lights.get(i).intensity);
				temp2.scale(Math.max(0, norm.dot(lightRay)));
				
				//specular scale factor
				temp3.scale(lights.get(i).intensity);
				temp3.scale(Math.pow(h.dot(norm), exponent));
				
				temp1.add(temp2);
				temp1.add(temp3);
			}
		}
		colorOut.set(temp1);
	}
	
public boolean isShadowed(Scene scene, Light light, Intersection inter, ArrayList<Surface> surfaces) {
		
		Ray shadowRay = new Ray();
		
		// Setup the shadow ray to start at surface and end at light
		shadowRay.origin.set(inter.intersection);
		shadowRay.direction.sub(light.position, inter.intersection);
		
		int i = 0;
		boolean intersection;
		do{
			intersection = surfaces.get(i).intersect(inter, shadowRay);
			if(intersection)
				return intersection;
		i++;	
		} while (i < surfaces.size());
		return false;
	}
	
	/**
	 * @see Object#toString()
	 */
	public String toString() {
		
		return "phong " + diffuseColor + " " + specularColor + " " + exponent + " end";
	}	
}
